Canigó - Servei d'Excepcions 2.2
SERVEI D'EXCEPCIONS
IntroduccióPropósitLa gestió d'excepcions permet informar que s'ha produït un error al realitzar una petició. Aquest error podrà ser tractat adequadament i en cas necessari informar a l'usuari, llençar una traça, enviar un correu, etc. La gestió correcta de les excepcions és molt important i crítica, però generalment la forma en la que es generen i getionen les excepcions en les aplicacions és un dels aspectes més ignorats en el disseny de les mateixes. L'ús apropiat de les excepcions fa que els nostres aplicatius siguin més robusts, més fàcils de desenvolupar i mantenir, més lliures d'errors i més fàcils de utilitzar. Per aquest motiu és important que donem el màxim de detall en les excepcions. Per evitar l'ús innecessari de blocs 'try-catch' dins el codi dels nostres aplicatius canigo proporciona un mecanisme d'intercepció, pel qual indicarem quines excepcions volem tractar i quins gestors les tractaran sense haver d'incorporar cap referència a cap classe de l'aplicació. Context i Escenaris d'ÚsEl Servei de Traces es troba dins dels serveis de Propósit General de canigo. El seu ús és imprescindible en canigo, ja que tots els serveis utilitzen la gestió d'excepcions definida per aquest servei.
Versions i DependènciesLes dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte: A qui va dirigitAquest document va dirigit als següents perfils:
Documents i Fonts de ReferènciaGlossariAOP (Aspect Oriented Programming) AOP permet definir funcionalitats comunes a diferents components d'un aplicatiu de forma única i amb un tractament general i localitzat, mitjançant la intercepció de la funcionalitat própia del mateix. Aquestes funcionalitats comunes es denominen aspectes. Aspectwerkz El codi d'intercepció s'afegeix de forma automàtica al bytecode de la classe original mitjançant una compilació adicional. Aquest 'bytecode' modificat canvia la forma de cridar els nostres mètodes sense canviar la funcionalitat, i afegir així els conceptes importants en la programació orientada a aspectes: joinpoint, advice, etc... Checked Exception Tipus d'excepció que hereta de java.lang.Exception. El llenguatge Java obliga a que aquestes excepcions hagin de ser capturades mitjançant un bloc 'try-catch' o bé declarar al mètode que es torna a llençar l'excepció (finalment algú l'haurà de capturar) mitjançant la clàusula 'throws'. Unchecked Exception A diferència de les checked exceptions, poden ser ignorades i per tant no és necessari capturar-les ni declarar-les, encara que com veurem posteriorment, és una bona pràctica declarar-les en la signatura del mètode. Descripció DetalladaArquitectura i Componentscanigo ofereix una arquitectura d'excepcions totalment deslligada de qualsevol implementació. Únicament es basa en les característiques de les excepcions del llenguatge Java. Els components podem classificar-los en:
Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls: JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-exceptions/apidocs/index.html Instal- lació i ConfiguracióInstal- lacióLa instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-exceptions' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'. ConfiguracióLa configuració del Servei d'Excepcions implica 3 pasos:
Definició dels gestors d'Excepcions Per cada gestor d'excepcions que volem crear (es pot crear un únic per una classe pare d'excepció, una per cada subtipus d'excepció, ..) crearem un bean de configuració amb la següent informació: Atributs:
Podem afegir propietats a cadascun dels gestors (per exemple el seu Servei de Traces) dins la definició del gestor. Exemple: <beanid="systemExceptionHandler" class="net.gencat.ctti.canigo.services.exceptions.handlers.SystemExceptionHandler"> Definició del mapeig de tipus d'excepcions i gestors Atributs:
Propietats:
Exemple:
<bean id="net.gencat.ctti.canigo.services.exceptions.aop.aspectwerkz.ExceptionHandlerAspect" class="net.gencat.ctti.canigo.services.exceptions.aop.aspectwerkz.ExceptionHandlerAspect" singleton="false"> <property name="exceptionHandlers"> <map> <entry> <key><value>net.gencat.ctti.canigo.services.exceptions.SystemException</value></key> <ref bean="systemExceptionHandler"/> <key><value>net.gencat.ctti.canigo.services.mail.exception.MailServiceException</value></key> <ref bean="unAltreExceptionHandler"/> ... <!-- tantes entrades com gestors --> </entry> </map> </property> </bean> A l'exemple es mostra cóm s'ha configurat el gestor 'systemExceptionHandler' per l'excepció de tipus 'net.gencat.ctti.canigo.services.exceptions.SystemException'. Definició del mecanisme d'intercepció automàtica ![]() Path desenvolupament: main/resources/aop.xml Aquest fitxer defineix: les característiques de l'AOP AspectWerkz per definir l'aspecte. Per a més informació es recomana consultar 'http://aspectwerkz.codehaus.org/'. Definirem els següents elements:
Aspecte
<!-- ExceptionHandlerAspect: classe que defineix la funcionalitat comuna implementada per aquest aspecte; </aspect>
Creació d'ExcepcionsLa creació d'excepcions és un aspecte de disseny molt important. Quants tipus d'excepcions generar? La resposta no és única i si bé existeixen diferents possibilitats la recomanació és:
A més, seguirem els següents patrons d'ús:
Declaració d'Excepcions llençades als mètodesSegons si l'excepció és de subtipus 'BusinessException' o de subtipus 'SystemException' es seguiran diferents estratègies:
En cas de que el mètode llenci una excepció de subtipus 'BusinessException' declarar 'throws Exception'. Com hem comentat, BusinessException és una excepció de tipus 'checked', el que implica que segons el mecanisme tradicional de Java qualsevol classe que rebi l'excepció hauria de control- lar-la mitjançant 'try-catch' o declarar-la de nou als seus mètodes (pràctica totalment prohibida). Segons el comentat, evitem el tractament innecessari declarant l'excepció amb 'throws Exception'. Aquesta incorporació és necessària per a que el compilador no es queixi de que no s'ha declarat l'excepció. Exemple:
public interface AccountBO { public void save(Account account) throws Exception; public void saveOrUpdate(Account account) throws Exception; public void update(Account account) throws Exception; public void delete(Account account) throws Exception ; public Account load(Account account); }
Si el mètode llença una excepció de tipus pare 'SystemException' hem de mantenir el mecanisme de Java, ja que aquestes excepcions, per ser de tipus 'unchecked' no requereixen del control al nivell superior. public void send(String from, String subject, String aMessage, boolean isHtml, String to) throws MailServiceException; Generació d'ExcepcionsLa generació d'excepcions es realitza mitjançant la clàusula 'throws' de Java. En la generació de les excepcions afegirem el màxim de detall possible mitjançant l'ús de la classe 'ExceptionDetails' (consultar l'apartat 'Arquitectura i Components' per a més informació). Aquesta classe permet que es pugui informar entre d'altres del nivell (Level), de la capa (Layer) i del subsistema (Subsystem) en que s'ha produit l'excepció que llencen l'excepció. A més, es pot informat un codi d'error (que tindrà la seva descripció en un fitxer de propietats) i unes propietats adicionals que serveixen per depurar l'error. El patró recomanat per llençar una excepció és el mostrat a continuació:
ExceptionDetails exDetails = new ExceptionDetails(...) exDetails.setProperties(...); ... throw new XXXException(exDetails); Encapsulació d'Excepcions rebudes de terceres partsSi volem integrar tercers components externs a canigo és convenient que s'encapsulin les excepcions rebudes en el mecanisme d'excepcions de canigo. El patró de tractament pot ser com el mostrat a continuació:
try { ... cridaServeiExtern(); } catch(ServeiExternException ex) { ExceptionDetails exDetails = new ExceptionDetails("codiExcepcio", ...,Layer.XXXX,Subsystem.XXXX); exDetails.setProperties(mailProperties); throw new XXXCanigoException(ex,exDetails);} Cóm veiem, capturem l'excepció del component de tercers i li afegim la informació necessària. Per últim llencem l'excepció incorporant com a segon paràmetre els detalls que hem indicat i en el primer paràmetre l'excepció arrel del tercer component. Exemple: package net.gencat.ctti.canigo.services.mail.impl; ... public class SpringMailServiceImpl implements MailService { ... public void send(String from, String subject, String aMessage, boolean isHtml, Map recipients, List attachments) throws MailException { ... try { ... message = this.mailSender.createMimeMessage(); ... helper.setFrom(from); helper.setSubject(subject); helper.setText(aMessage,isHtml); ... this.mailSender.send(message); } catch(MessagingException ex){ ExceptionDetails exDetails = new ExceptionDetails("canigo.services.mail.error_preparing_addresses", null,Layer.SERVICES, Subsystem.MAIL_SERVICES); exDetails.setProperties(mailProperties); throw new MailServiceException(ex,exDetails); } catch(Exception ex){ ExceptionDetails exDetails = new ExceptionDetails("canigo.services.mail.error_sending_mail", null,Layer.SERVICES, Subsystem.MAIL_SERVICES); exDetails.setProperties(mailProperties); throw new MailServiceException(ex,exDetails); } } Intercepció i tractament de les ExcepcionsMitjançant l'ús de la programació orientada a aspectes (AOP) i les classes proporcionades pel servei d'excepcions s'obté una solució integral a la gestió intel.ligent d'excepcions. La intercepció de les excepcions es realitza mitjançant el mecanisme definit als fitxers de configuració (veure apartat 'Configuració i Instal- lació). Per definir un gestor s'han de seguir els següents pasos:
Exemple:
package net.gencat.ctti.canigo.services.exceptions.handlers; package net.gencat.ctti.openframe.services.exceptions.handlers; ... public class SystemExceptionHandler extends ExceptionHandlerAdapter { ... public void handleException(Throwable e) throws Throwable { Log log = this.loggingService.getLog(this.getClass()); log.debug("Exception received: SystemExceptionHandler, Exception catched:" + e); throw e; } ... } Eines de SuportGeneració del bytecode d'intercepció amb AspectWerkzPer tal de que funcioni en execució el mecanisme d'intercepció cal un procés adicional de compilació a les classes. Aquest procés adicional es pot executar en Maven mitjançant el goal 'aspectwerkz:weave' tal i com es mostra en la figura següent:
![]() Aquest procés fa servir les següents variables de configuració que hauran d'estar en el nostre fitxer 'project.properties': <!-- mode de màxima informació en el procés de modificació de bytecode --> maven.aspectwerkz.verbose=true <!-- compilació basada en anotacions --> maven.aspectwerkz.mode=attribdef maven.aspectwerkz.definition.validate=true <!-- fitxer de propietats del servei (veure Configuració) o de definició del nostre aspecte --> maven.aspectwerkz.definition.file.src=$\{basedir\}/src/main/resources/aop.xml <!-- directori a on es copien totes les classes que s'han de passar pel nostre procés addicional de modificació de bytecode --> maven.aspectwerkz.build.dest=$\{basedir\}/target/aspectwerkz/classes <!-- directori a on s'ubicaran les classes modificades. És important que aquest directori sigui el directori WEB-INF/classes de l'aplicació desplegada al servidor --> maven.aspectwerkz.weave.build.dir=$\{basedir\}/.deployables/$\{ ctti.war.wtp.module\}/WEB-INF/classes Per evitar la necessitat manual de realitzar aquest goal, podem incorporar el següent codi 'Maven' en la creació de l'aplicatiu Web o de la llibreria: <u:available file="$\{maven.aspectwerkz.weave.build.dir\}/aop.xml"> <attainGoal name="aspectwerkz:weave"/> <ant:echo>Moving file $\{maven.aspectwerkz.weave.build.dir\}/aop.xml to $\{maven.aspectwerkz.weave.build.dir\}/../aop.xml</ant:echo> <ant:move failonerror="false" file="$\{maven.aspectwerkz.weave.build.dir\}/aop.xml" tofile="$\{maven.aspectwerkz.weave.build.dir\}/../aop.xml"/> </u:available> Integració amb Altres ServeisIntegració amb el Servei de Presentació (Arquitectura Base)Aplicar les estratègies definides prèviament implica que l'excepció és llençada a les capes superiors. Aquestes poden decidir si tractar-la o no tractar-la. Si la petició ha estat realitzada per un usuari en mode on-line és convenient que li informem de l'excepció que s'ha produit. Tota excepció produïda per l'aplicació arriba finalment a la classe principal de l'arquitectura de canigo 'ExtendedDelegatingTilesRequestProcessor' (veure el document 'Serveis de Presentació' per a més informació). Aquesta classe captura l'excepció rebuda i realitza els següents pasos:
message = new ActionMessage(exDetails.getErrorCode(), Afegeix aquest missatge a l'objecte 'ActionMessages' amb clau 'org.apache.struts.action.ERROR'.
request.setAttribute(Globals.ERROR_KEY, messages); Els errors que s'insereixen fan ús del Servei Multidioma. És necessari afegir al fitxer de multiidioma les següents entrades:
net.gencat.ctti.canigo.services.exceptions.WrappedCheckedException=<br>Error type=\{0\} <br>Localized message=\{1\}<br>Properties=\{2\}<br> Layer=\{3\}<br>Subsystem=\{4\} <br>Error code=\{5\}<br>Arguments=\{6\} net.gencat.ctti.canigo.services.exceptions.SystemException=<br>Error type=\{0\} <br>Localized message=\{1\}<br>Properties=\{2\}<br> Layer=\{3\}<br>Subsystem=\{4\} <br>Error code=\{5\}<br>Arguments=\{6\}
Pàgina per presentar els errors Podem representar els errors en una pàgina JSP fent ús del tag 'messages' de Struts. A continuació es mostra un exemple de la seva utilització, on es mostren els missatges en colors diferents segons la severitat de l'error:
<html:messages id="msg" property="org.apache.struts.action.ERROR"> <div class="error" style="margin-right: 10px; margin-bottom: 3px; margin-top: 3px"> <img src="images/iconWarning.gif" class="icon" alt="Warning" /> <bean:write name="msg" filter="false"/><a href="javascript:showEL('errorDetails')"> <b><bean:message key="jsp.includes.see_error_details"/></b></a> <a href="javascript:showEL('stackTrace')" > <b><bean:message key="jsp.includes.see_stack_trace"/></b></a>]<br> <html:messages id="msg" property="net.gencat.ctti.canigo.services.exceptions.Level.WARNING"> <div id="errorDetails" style="display: none"><br><font color="#ff0000"> <bean:write name="msg" filter="false"/></font> </div> </html:messages> <html:messages id="msg" property="net.gencat.ctti.canigo.services.exceptions.Level.ERROR"> <div id="errorDetails" style="display: none"> <font color="#00cc00"><bean:write name="msg" filter="false"/></font> </div> </html:messages> <html:messages id="msg" property="net.gencat.ctti.canigo.services.exceptions.STACKTRACE"> <div id="stackTrace" style="display: none"><br> <pre><bean:write name="msg" filter="false"/></pre> </div> </html:messages> </div> </html:messages> |